#include "Debug.h"
#include "DistortionModel.h"
#define _CRTDBG_MAP_ALLOC 
#include <stdlib.h>
#include <crtdbg.h>



DistortionModel::DistortionModel(MItMeshPolygon* faceIn, MItMeshVertex* vertIn, MItMeshEdge* edgeIn, MFnMesh* _mesh)
{
	faceIt = faceIn;
	vertsIt = vertIn;	
	mesh = _mesh;
	edgeIt = edgeIn;
	createSubMeshSpace();

	//printSubMeshSpace();
}

DistortionModel::~DistortionModel(void)
{
	for (map<int, TriangleBase*>::iterator it=triangles.begin(); it!=triangles.end(); ++it)		
		delete it->second;	
	for (map<int, VertexBase*>::iterator it=vertices.begin(); it!=vertices.end(); ++it)		
	{
		it->second->cleanMemory();
		delete it->second;	
	}
	for (map<int, EdgeBase*>::iterator it=edges.begin(); it!=edges.end(); ++it)		
		delete it->second;	
	
}
 
double DistortionModel::getOveralldistortion(int maxRadius, int colType)
{
	vertsIt->reset();

	for(int i = 0; i<vertsIt->count(); i++ )
	{
		getDistortionAtVertex(vertices[i], maxRadius);
		if(i%200 ==0)
		  cout << "Processing Vert "  << i << endl;
	}
	
	filterDistortion();	

	double totalDistortionSummed = 0;
	for (int i = 0; i<vertsIt->count(); i++ )
	{
		totalDistortionSummed += vertices[i]->getDistortion();			
		//cout << vertices[i]->getNum() << " " << vertices[i]->getDistortion() << endl;		
	}

	cout << "Total Distortion: " << totalDistortionSummed << endl;
	
	//paintDistortionOnMesh(colType);
	
	return totalDistortionSummed;
}

double DistortionModel::getDistortionAtVertex(VertexBase* vertex, int maxRadius)
{
	double maxDistortion = -20000;
	int maxDistortionRadius =0;
	vector<SubMesh*> submeshes;
	submeshes.resize(maxRadius);
	for(int i=0; i<maxRadius; i++)
	{
		double R = i * getLongestEdgeLength(vertex);
		if(i == 0)
			submeshes[i] = new SubMesh(R, vertex, triangles, NULL);
		else
			submeshes[i] = (new SubMesh(R, vertex, triangles, submeshes[i-1]));

		double dist = submeshes[i]->getDistortionAtVertexAtRadiusFromSubmesh();
		//cout << "Vertex: " << vertex->getNum() << " Pre Submesh: " << submeshes[i]->isValid() << " Dist: " << dist << endl;
		
		if( !submeshes[i]->isValid() || (vertex->onBoundary() && dist == 0)) //Submesh is not valid or it's on the boundary					
			dist = 0;				
				
		if(i == 0)
			vertex->setRad0Distortion(dist);
		
		if(dist > maxDistortion)
		{
			maxDistortion = dist;
			maxDistortionRadius = i;
		}	
		
	
		if( !submeshes[i]->isValid() || (vertex->onBoundary() && dist == 0))
			break;
	}	
	vertex->setDistortion(maxDistortion, maxDistortionRadius,  maxDistortionRadius * getLongestEdgeLength(vertex), submeshes[maxDistortionRadius] );
	for(int i=0; i<submeshes.size(); i++)
	{
		if(i != maxDistortionRadius)
		  delete(submeshes[i]);
	}
	return maxDistortion;
}

double DistortionModel::getLongestEdgeLength(VertexBase* vertex)
{
	//region radius R = r * length of longest edge from n to any other vertex
	MIntArray indexList;
	Utilities::vertsItGet(vertex->getNum())->getConnectedVertices(indexList);

	double maxDist = 0;
	for(uint i=0; i<indexList.length(); i++)
		if(vertex->getPos().distanceTo(Utilities::vertsItGet(indexList[i])->position()) > maxDist)
			maxDist = vertex->getPos().distanceTo(Utilities::vertsItGet(indexList[i])->position());
	return maxDist;
}

void DistortionModel::createSubMeshSpace()
{
	int counter =0;
	vertsIt->reset();
	faceIt->reset();
	edgeIt->reset();
	while(!vertsIt->isDone())
	{
		int index = vertsIt->index();		
		VertexBase* newCorner = new VertexBase(index, vertsIt->position());
		if(vertsIt->onBoundary())
			newCorner->setBoundary(true);
		vertices[index] = newCorner;
		MIntArray connections;
		vertsIt->getConnectedEdges(connections);
		newCorner->setConnectedEdges(connections);
		vertsIt->next();
	}	
	while(!edgeIt->isDone())
	{
		int index = edgeIt->index();		
		EdgeBase* newEdge = new EdgeBase(index, vertices[edgeIt->index(0)], vertices[edgeIt->index(1)], edgeIt->onBoundary());
		edges[index] = newEdge;
		edgeIt->next();
	}	
	while(!faceIt->isDone())
	{	
		MIntArray verticesIndices;
		faceIt->getVertices(verticesIndices);		
		MIntArray edgeIndices;
		faceIt->getEdges(edgeIndices);
	
		VertexBase* aCorner = vertices[verticesIndices[0]];
		VertexBase* bCorner = vertices[verticesIndices[1]];
		VertexBase* cCorner = vertices[verticesIndices[2]];

		EdgeBase* abEdge = edges[edgeIndices[0]];
		EdgeBase* bcEdge = edges[edgeIndices[1]];
		EdgeBase* caEdge = edges[edgeIndices[2]];

		TriangleBase* tri = new TriangleBase(faceIt, vertsIt, faceIt->index(), aCorner, bCorner, cCorner, abEdge, bcEdge, caEdge);
		triangles[faceIt->index()] = tri;
		faceIt->next();
	}
}

void DistortionModel::printSubMeshSpace()
{
	cout << "-----------------------------" << endl;	
		vertices[0]->printAllContents();
	cout << "-----------------------------" << endl;
}

void DistortionModel::paintDistortionOnMesh(int colType)
{
	cout << "Colouring Mesh Type " << colType << endl; 
	
	if(colType == 0 )
	{
		mesh->clearColors(); 
		MIntArray faceIds;
		MColorArray colors;
		for (map<int, TriangleBase*>::iterator it=triangles.begin(); it!=triangles.end(); ++it)	
		{
			float percentageDistortion =0;
			for(int i=0; i<3; i++)
				percentageDistortion += (float)(it->second->getCorners()[i]->getDistortion());
			percentageDistortion *= 4;
			MColor shading(1-percentageDistortion, 1-percentageDistortion, 1);
			colors.append(shading);
			faceIds.append(it->second->getNum());
		}	
		mesh->setFaceColors(colors, faceIds);	
	}
	else
	{
		mesh->clearColors(); 
		MIntArray faceIds;
		MColorArray colors;
		for (map<int, VertexBase*>::iterator it=vertices.begin(); it!=vertices.end(); ++it)	
		{				
			if(it->second->getRadius() != 0)
			{
				//cout << "New distortion Region, center:" << it->second->getNum() << "  radius:" << it->second->getRadius() << ":- " ;
				SubMesh* sm = it->second->getSubMesh();	
				MColor color(((double)(rand()%100))/100, ((double)(rand()%100))/100, ((double)(rand()%100))/100);
				vector<TriangleBase*> tris = sm->getTriangles();
				for(int i=0; i<tris.size(); i++)	
				{	
					colors.append(color);
					faceIds.append(tris[i]->getNum());
				}				
			}	
		}
		mesh->setFaceColors(colors, faceIds);
	}
}

void DistortionModel::filterDistortion()
{
	cout << "Filtering Distortion------------" << endl;	
	for (map<int, VertexBase*>::iterator it=vertices.begin(); it!=vertices.end(); ++it)	
	{		
		VertexBase* n = it->second;	
		if(n->getRadius() != 0 && n->getSubMesh()->isValid() )				
		{
			vector<VertexBase*> verts = n->getSubMesh()->getVerticies();
			for (int i=0; i<verts.size(); i++)	
			{					
				VertexBase* m = verts[i];	
				if( n->getNum() != m->getNum() )								
				{										
					if( (m->getDistortion() == n->getDistortion()) || ((m->getDistortion() < n->getDistortion()) && (1.05*m->getDistortion() > n->getDistortion())) || ((m->getDistortion() > n->getDistortion()) && (0.95*m->getDistortion() < n->getDistortion())) )						
					{		
						if(n->getRad0Distortion() > m->getRad0Distortion() )					
							m->setDistortionToRad0();							
						else				
						{							
							n->setDistortionToRad0();	
							break;
						}
					}
					if( n->getDistortion() > m->getDistortion() )					
						m->setDistortionToRad0();							
				}
			}			
		}
	}
	cout << "Filtering Distortion End--------" << endl;
}

vector<VertexBase*> DistortionModel::getVertices()
{
	vector<VertexBase*> verts;	
	for (map<int, VertexBase*>::iterator it=vertices.begin(); it!=vertices.end(); ++it)		
		verts.push_back(it->second);
	return verts;
}

vector<EdgeBase*> DistortionModel::getEdges()
{
	vector<EdgeBase*> edgesVect;	
	for (map<int, EdgeBase*>::iterator it=edges.begin(); it!=edges.end(); ++it)		
		edgesVect.push_back(it->second);
	return edgesVect;
}

map<int,VertexBase*> DistortionModel::getVertsMap()
{
	return vertices;
}

map<int,EdgeBase*> DistortionModel::getEdgeMap()
{
	return edges;
}
